package com.skp.shaphan.ui;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;

import com.skp.job.ProjectJobException;
import com.skp.shaphan.SQLEditorsPane;
import com.skp.shaphan.ShaphanApp;
import com.skp.shaphan.actions.ExecuteSQLAction;
import com.skp.shaphan.actions.HighlightSQLAction;
import com.skp.shaphan.actions.SQLEditorLayoutAction;
import com.skp.shaphan.layout.SQLEditorLayout;
import com.skp.shaphan.layout.XYConstraints;
import com.skp.shaphan.model.ConnectionDetails;
import com.skp.shaphan.toolbars.SqlEditorToolbar;

/**
 * An interface for editing SQL
 * @author Stephen Phillips
 *
 */
public class SQLEditor extends JPanel implements KeyListener, DocumentListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 8515030463987593582L;
	private JTextArea sqlStatement;
	private Color backgroundColor;
	private HighlightSQLAction highlightSQLAction = null;
	private boolean sqlExecuted;
	private SQLEditorLayoutAction layoutAction = null;
	private boolean toolbarVisible = false;

	private boolean mousedOver;
	private SqlEditorToolbar toolbar;
	
	public SQLEditor() {
		//this.setPreferredSize(new Dimension(500, 200));
		//this.setLayout(new GridBagLayout());
		this.setLayout(new SQLEditorLayout());
		sqlStatement = new JTextArea();
		sqlStatement.setFont(new Font("Courier", Font.PLAIN, 12));
		backgroundColor = sqlStatement.getBackground();
		LineNumberedBorder lineNumbers = new LineNumberedBorder(LineNumberedBorder.LEFT_SIDE, LineNumberedBorder.LEFT_JUSTIFY);
		sqlStatement.setBorder(lineNumbers);
		toolbar = new SqlEditorToolbar(this);
		//this.add(toolbar, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
		//this.add(sqlStatement, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
		add(toolbar, new XYConstraints("0", "0", "100%", "24"));
		add(sqlStatement, new XYConstraints("0", "27", "100%", ""));
		
		// reset for the state
		sqlExecuted = false;
		mousedOver = false;
		
		sqlStatement.addKeyListener(this);
		sqlStatement.getDocument().addDocumentListener(this);
		this.doLayout();
	}
	
	/**
	 * Determines what the color of the sql statement should be based on the state
	 */
	private void determineColor() {
		Color color;
		if(sqlExecuted) {
			if(mousedOver) {
				color = new Color(255, 200, 255, 255);
			} else {
				color = new Color(255, 200, 200, 255);
			}
		} else {
			if(mousedOver) {
				color = new Color(200, 200, 255, 255);
			} else {
				color = new Color(255, 255, 255, 255);
			}
		}
		this.setBackgroundColor(color);
	}

	public Color getBackgroundColor() {
		return backgroundColor;
	}

	protected void setBackgroundColor(Color backgroundColor) {
		this.backgroundColor = backgroundColor;
		if(highlightSQLAction == null) {
			highlightSQLAction = new HighlightSQLAction(this);
		}
		if(!highlightSQLAction.isRunning()) {
			ShaphanApp.getMainWindow().getMainTimer().addAction(highlightSQLAction);
		}
	}
	
	public void setToolbarVisible(boolean newValue) {
		this.toolbarVisible = newValue;
		doLayout();
	}
	
	public boolean getToolbarVisible() {
		return toolbarVisible;
	}
	
	public void startLayoutAnimation() {
		if(layoutAction == null) {
			layoutAction = new SQLEditorLayoutAction(this); 
		}
		if(!layoutAction.isRunning()) {
			ShaphanApp.getMainWindow().getMainTimer().addAction(layoutAction);
		}
	}
	
	public Color getSQLBackgroundColor() {
		return sqlStatement.getBackground();
	}
	
	public void setSQLBackgroundColor(Color color) {
		sqlStatement.setBackground(color);
	}

	public ConnectionDetails getConnection() {
		SQLEditorsPane pane = (SQLEditorsPane) this.getParent().getParent().getParent().getParent();
		return pane.getConnection();
	}

	public void setConnection(ConnectionDetails connection) {
		SQLEditorsPane pane = (SQLEditorsPane) this.getParent().getParent().getParent().getParent();
		pane.setConnection(connection);
	}

	public String getSQL() {
		return sqlStatement.getText();
	}

	public void setSQL(String sql) {
		sqlStatement.setText(sql);
	}

	@Override
	public void keyPressed(KeyEvent e) {
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			int currentLine = getCurrentLineNumber();
			if(currentLine <= 1) {
				SQLEditorsPane editors = ShaphanApp.getMainWindow().getSQLEditorsPane();
				int row = editors.getRowForSQLEditor(this);
				SQLEditor previous = editors.getSQLEditor(row - 1);
				if(previous != null) {
					int currentPosition = this.getCurrentColumn();
					previous.requestFocus();
					previous.setCurrentColumn(currentPosition);
				}
				e.consume();
			}
		} else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			int currentLine = getCurrentLineNumber();
			if(currentLine >= sqlStatement.getLineCount()) {
				SQLEditorsPane editors = ShaphanApp.getMainWindow().getSQLEditorsPane();
				int row = editors.getRowForSQLEditor(this);
				SQLEditor next = editors.getSQLEditor(row + 1);
				if(next != null) {
					int currentPosition = this.getCurrentColumn();
					next.requestFocus();
					next.setCurrentColumn(currentPosition);
				}
				e.consume();
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		if(e.getKeyCode() == KeyEvent.VK_ENTER && (e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
			ExecuteSQLAction action = new ExecuteSQLAction();
			action.setEditor(this);
			action.setResultPane(ShaphanApp.getMainWindow().getResultPane());
			action.start();
		}
	}
	
	public void resizeToFit() {
		Dimension d = new Dimension(sqlStatement.getPreferredSize());
		d.height = sqlStatement.getFontMetrics(sqlStatement.getFont()).getHeight() * sqlStatement.getLineCount();
		d.height += 10;
		//this.setPreferredSize(d);
		sqlStatement.setPreferredSize(d);
		d = new Dimension(d);
		d.height = sqlStatement.getY() + d.height;
		//this.setPreferredSize(d);
		//System.out.println("Setting preferred size -- sqlStatement Y = " + sqlStatement.getY() + " height = " + sqlStatement.getPreferredSize().height);
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void changedUpdate(DocumentEvent e) {
		resizeToFit();
	}
	
	public void markAsRan() {
		getParentEditorsPane().markAsRan(this);
	}
	
	public SQLEditorsPane getParentEditorsPane() {
		Container parent = this.getParent();
		while(parent != null) {
			if(parent instanceof SQLEditorsPane) {
				return (SQLEditorsPane)parent;
			}
			parent = parent.getParent();
		}
		return null;
	}

	@Override
	public void insertUpdate(DocumentEvent e) {
		try {
			Document doc = e.getDocument();
			String insertedText = doc.getText(e.getOffset(), e.getLength());
			String previousText = doc.getText(0, e.getOffset());
			int newLinePos = insertedText.indexOf('\n');
			if(newLinePos >= 0) {
				newLinePos += e.getOffset();
			}
			int semicolonPos = previousText.lastIndexOf(';');
			if(newLinePos >= 0 && semicolonPos >= 0) {
				SQLEditorsPane parent = getParentEditorsPane();
				//SQLEditor newEditor = parent.insertSQLEditor(parent.getRowForSQLEditor(this) + 1);
				SQLEditor newEditor = parent.insertSQLEditor(this);
				this.scrollRectToVisible(newEditor.getBounds());
				newEditor.setSql(doc.getText(newLinePos + 1, doc.getLength() - newLinePos).trim());
				final String adjustedSQL = doc.getText(0, newLinePos);
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						setSQL(adjustedSQL);
					}
				});
				newEditor.requestFocus();
			}
			this.resizeToFit();
		} catch (BadLocationException e1) {
			(new ProjectJobException(e1)).displayException();
		}
	}

	public void setSql(String newSql) {
		sqlStatement.setText(newSql);
		resizeToFit();
	}

	@Override
	public void removeUpdate(DocumentEvent e) {
		resizeToFit();
	}

	@Override
	public synchronized void addFocusListener(FocusListener l) {
		// This won't get focus -- the text field will so ...
		// put the focus listener there
		sqlStatement.addFocusListener(l);
	}

	@Override
	public void requestFocus() {
		// This won't get focus -- the text field will so ...
		// pass the request through to the sqlStatement
		sqlStatement.requestFocus();
	}

	@Override
	public boolean requestFocus(boolean temporary) {
		// This won't get focus -- the text field will so ...
		// pass the request through to the sqlStatement
		return sqlStatement.requestFocus(temporary);
	}
	
	// from: http://swingworker.blogspot.com/2007/12/line-number-in-jtextarea.html
	public int getCurrentLineNumber() {
	    int line;
	    int caretPosition = sqlStatement.getCaretPosition();
	    Element root = sqlStatement.getDocument().getDefaultRootElement();
	    line = root.getElementIndex(caretPosition) + 1;
	    return line;
	}
	
	public int getCurrentColumn() {
	    int caretPosition = sqlStatement.getCaretPosition();
	    Element root = sqlStatement.getDocument().getDefaultRootElement();
		return caretPosition - root.getElement(root.getElementIndex(caretPosition)).getStartOffset();
	}
	
	public void setCurrentColumn(int position) {
	    int caretPosition = sqlStatement.getCaretPosition();
	    Element root = sqlStatement.getDocument().getDefaultRootElement();
	    Element current = root.getElement(root.getElementIndex(caretPosition));
	    if(current.getEndOffset() < position) {
	    	position = current.getEndOffset() - 1;
	    }
	    sqlStatement.setCaretPosition(current.getStartOffset() + position);
	}

	public boolean isSqlExecuted() {
		return sqlExecuted;
	}

	public void setSqlExecuted(boolean sqlExecuted) {
		this.sqlExecuted = sqlExecuted;
		this.determineColor();
	}

	public boolean isMousedOver() {
		return mousedOver;
	}

	public void setMousedOver(boolean mousedOver) {
		this.mousedOver = mousedOver;
		this.determineColor();
		this.setToolbarVisible(mousedOver);
	}
	
	public int getCorrectWidth() {
		Insets i = getInsets();
		Insets i2 = sqlStatement.getInsets();
		return sqlStatement.getFontMetrics(sqlStatement.getFont()).stringWidth(getSQL()) + i.left + i.right + i2.left + i2.right;
	}
	
}
